
class RenderedComponent(object):
  def __init__(self, material):
    self.material = material 
    self.name = material.name
    if material.bottle_num:
      self.bottle_num = material.bottle_num
    else:
      self.bottle_num = '-'
    self.rendered_amount = 0.0
    self.rendered_price = 0.0

  def formatted_price(self):
    return '%.2f' % self.rendered_price

  def formatted_amount(self):
    return '%.3f' % self.rendered_amount

  def Finalize(self):
    if self.material and self.material.price_ml:
      ppml = self.material.price_ml
    else:
      # Default price per ml, based on Linda's store, typically $15 for 80ml.
      ppml = 0.1875
    self.rendered_price = ppml * self.rendered_amount

  def SortByName(a, b):
    return cmp(a.name, b.name)

  def SortByPriceDesc(a, b):
    if b.rendered_price == a.rendered_price:
      return cmp(a.name, b.name)
    return cmp(b.rendered_price, a.rendered_price)

  def SortByAmountDesc(a, b):
    if b.rendered_amount == a.rendered_amount:
      return cmp(a.name, b.name)
    return cmp(b.rendered_amount, a.rendered_amount)

  def SortByBottle(a, b):
    return cmp(a.bottle_num, b.bottle_num)

class Renderer(object):
  def __init__(self, material, requested_total, sort_key='a'):
    self.material = material
    self.total_amount = 0
    self.total_price = 0
    self.filler = 0
    self.sort_key = sort_key

    # map of material_id to RenderedComponent, used for aggregation.
    self.component_map = {}

    self.Render(requested_total)

  def rendered_components(self):
    comps = self.component_map.values()
    if self.sort_key == 'a':
      comps.sort(cmp=RenderedComponent.SortByAmountDesc)
    elif self.sort_key == 'n':
      comps.sort(cmp=RenderedComponent.SortByName)
    elif self.sort_key == 'p':
      comps.sort(cmp=RenderedComponent.SortByPriceDesc)
    elif self.sort_key == 'b':
      comps.sort(cmp=RenderedComponent.SortByBottle)
    else:
      print 'Unknown sort key', self.sort_key
      comps.sort(cmp=RenderedComponent.SortByAmountDesc)
    return comps

  def Render(self, requested_total):
    ids_seen = set()
    accord_amount = self.material.GetTotalAmount()
    if accord_amount == 0:
      return
    scale = requested_total / accord_amount
    self.component_map = self.material.GetRenderedComponents(scale, ids_seen)
    for component in self.component_map.values():
      component.Finalize()
      self.total_amount += component.rendered_amount
      self.total_price += component.rendered_price
    self.filler = requested_total - self.total_amount


